#include <iostream>
#include <mutex>

using namespace std;

template<typename T>
class My_shared_ptr {
public:
    // 构造函数 - 申请资源
    My_shared_ptr(T *ptr)
            : ptr_(ptr), pCount_(new int(1)), pmtx_(new mutex) {}

    // 拷贝构造
    My_shared_ptr(const My_shared_ptr<T> &rhs)
            : ptr_(rhs.ptr_), pCount_(rhs.pCount_), pmtx_(rhs.pmtx_) {
        // 增加引用计数
        addCount();
    }

    // 重载赋值运算符
    My_shared_ptr& operator=(const My_shared_ptr<T>& rhs) {
        if (this != &rhs) { // 避免自复制
            release(); // 先释放自己本身

            // 复制
            ptr_ = rhs.ptr_;
            pCount_ = rhs.pCount_;
            pmtx_ = rhs.pmtx_;

            // 引用计数+1
            addCount();
        }
	return *this;
    }

    // 引用计数+1
    void addCount() {
        pmtx_->lock();
        ++(*pCount_);
        pmtx_->unlock();
    }

    // 释放资源
    // 需要单独写成一个函数，不能放到析构函数里面，因为赋值运算符函数中也需要
    void release() {
        // 需要释放的资源有三种：管理的指针，引用计数，锁
        pmtx_->lock();
        // 这里进行了上锁，在解锁之前没有办法释放锁资源，所以需要增加一个标志位
        bool delete_lock = false;

        if (--(*pCount_) == 0) {
            // 引用计数为0了，才释放管理的指针以及引用计数的空间
            delete ptr_;
            delete pCount_;

            delete_lock = true; // 此时也需要释放锁，但不是在这里释放
        }
        pmtx_->unlock();

        if (delete_lock) {
            delete pmtx_;
        }
    }

    // 析构函数 - 释放资源
    ~My_shared_ptr() {
        release();
    }

    // 重载*
    T& operator*() {
        return *ptr_;
    }

    // 重载->
    T* operator->() {
        return ptr_;
    }

    // 返回引用计数
    int use_count() const {
        return *pCount_;
    }

private:
    T *ptr_;
    int *pCount_;
    mutex *pmtx_;
};


void test() {
    My_shared_ptr<int> sp(new int(100));
    cout << "*sp = " << *sp << endl;
    cout << "sp.use_count() = " << sp.use_count() << endl;

    My_shared_ptr<int> sp2(sp);
    cout << "after sp2(sp)" << endl;
    cout << "*sp2 = " << *sp2 << endl;
    cout << "sp.use_count() = " << sp.use_count() << endl;
    cout << "sp2.use_count() = " << sp2.use_count() << endl;

    My_shared_ptr<int> sp3(new int(10));
    cout << "*sp3 = " << *sp3 << endl;
    cout << "sp3.use_count() = " << sp3.use_count() << endl;
    sp = sp3;
    cout << "after sp = sp3" << endl;
    cout << "*sp = " << *sp << endl;
    cout << "*sp2 = " << *sp2 << endl;
    cout << "*sp3 = " << *sp3 << endl;
    cout << "sp.use_count() = " << sp.use_count() << endl;
    cout << "sp2.use_count() = " << sp2.use_count() << endl;
    cout << "sp3.use_count() = " << sp3.use_count() << endl;
}

int main(void) {
    test();
    return 0;
}
